home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / COMM / ASYNEW / ASYNC4U.PAS < prev   
Pascal/Delphi Source File  |  1988-03-04  |  18KB  |  449 lines

  1. {----------------------------------------------------------------------}
  2. {                          ASYNC4U.PAS                                 }
  3. {                                                                      }
  4. {  This is a faithful translation of the famous ASYNC.INC by Michael   }
  5. {  Quinlan into a Turbo 4.0 unit.  No extra frills, no modification of }
  6. {  types, nothing fancy.  But with this code you should be able to     }
  7. {  delete your $I ASYNC.INC directive, add a USES ASYNC4U statement,   }
  8. {  and recompile your existing program.  If you want to add support    }
  9. {  for more ports, other computers, or change to use the new data      }
  10. {  types, all good ideas, go right ahead. With this you don't have to. }
  11. {                                                                      }
  12. {                                      Scott Gurvey, November 29 1987  }
  13. {                                                                      }
  14. {  I have taken the liberty of modifying Scott's original translation  }
  15. {  to make use of some of TURBO 4's nicer features.  To wit:           }
  16. {  The procedure that initializes this unit's variables has been moved }
  17. {  to the startup initialization section of the unit, removing the need}
  18. {  to call ASYNC_INIT.                                                 }
  19. {  The unit now defines an ExitProc routine that will call ASYNC_CLOSE }
  20. {  whenever a termination occurs.  The procedure ASYNC_CLOSE may still }
  21. {  be explicitly called.  However, if an abnormal termination should   }
  22. {  occur, the system won't be left with interrupt handlers active.     }
  23. {                                                                      }
  24. {                                     Tom R. Donnelly, March 4, 1988   }
  25. {----------------------------------------------------------------------}
  26. {                                                                      }
  27. {                          ASYNC.INC                                   }
  28. {                                                                      }
  29. {  Async Communication Routines                                        }
  30. {  by Michael Quinlan                                                  }
  31. {  with a bug fixed by Scott Herr                                      }
  32. {  made PCjr-compatible by W. M. Miller                                }
  33. {  Highly dependant on the IBM PC and PC DOS 2.0                       }
  34. {                                                                      }
  35. {  Moved Async_init into Unit Initialization Section                   }
  36. {  Setup ExitProc to call Async_close, to reset ports and vectors      }
  37. {                                                                      }
  38. {  based on the DUMBTERM program by CJ Dunford in the January 1984     }
  39. {  issue of PC Tech Journal.                                           }
  40. {                                                                      }
  41. {  Entry points:                                                       }
  42. {                                                                      }
  43. {    Async_Init                 (Moved to unit initialization)         }
  44. {      Performs initialization.                                        }
  45. {                                                                      }
  46. {    Async_Open(Port, Baud : Integer;                                  }
  47. {               Parity : Char;                                         }
  48. {               WordSize, StpBits : Integer) : Boolean                 }
  49. {      Sets up interrupt vector, initialies the COM port for           }
  50. {      processing, sets pointers to the buffer.  Returns FALSE if COM  }
  51. {      port not installed.                                             }
  52. {                                                                      }
  53. {    Async_Buffer_Check(var C : Char) : Boolean                        }
  54. {      If a character is available, returns TRUE and moves the         }
  55. {        character from the buffer to the parameter                    }
  56. {      Otherwise, returns FALSE                                        }
  57. {                                                                      }
  58. {    Async_Send(C : Char)                                              }
  59. {      Transmits the character.                                        }
  60. {                                                                      }
  61. {    Async_Send_String(S : LStr)                                       }
  62. {      Calls Async_Send to send each character of S.                   }
  63. {                                                                      }
  64. {    Async_Close                                                       }
  65. {      Turn off the COM port interrupts.                               }
  66. {      **MUST** BE CALLED BEFORE EXITING YOUR PROGRAM; otherwise you   }
  67. {      will see some really strange errors and have to re-boot.        }
  68. {      (Note: The **MUST** restriction no longer applies.  This unit   }
  69. {       will hook itself into the ExitProc chain to automatically      }
  70. {       close the com ports, if still open at termination.)            }
  71. {                                                                      }
  72. {----------------------------------------------------------------------}
  73. {$B-} { Short circuit boolean ON }
  74. {$I-} { I/O hecking OFF }
  75. {$R-} { Range checking OFF }
  76. {$S-} { Stack checking OFF }
  77. {$V-} { Var-str checking OFF}
  78.  
  79. UNIT ASYNC4U;
  80.  
  81. INTERFACE
  82.  
  83. USES Dos;
  84.  
  85. { global declarations }
  86.  
  87. type
  88.   LStr = String[255];  { generic string type for parameters }
  89.  
  90. const
  91.   Async_Buffer_Max = 4095;
  92.  
  93. var
  94.   Async_OriginalVector : pointer;
  95.   Async_Buffer       : Array[0..Async_Buffer_Max] of char;
  96.  
  97.   Async_Open_Flag    : Boolean;   { true if Open but no Close }
  98.   Async_Port         : Integer;   { current Open port number (1 or 2) }
  99.   Async_Base         : Integer;   { base for current open port }
  100.   Async_Irq          : Integer;   { irq for current open port }
  101.  
  102.   Async_Buffer_Overflow : Boolean;  { True if buffer overflow has happened }
  103.   Async_Buffer_Used     : Integer;
  104.   Async_MaxBufferUsed   : Integer;
  105.  
  106.     { Async_Buffer is empty if Head = Tail }
  107.   Async_Buffer_Head  : Integer;   { Locn in Async_Buffer to put next char }
  108.   Async_Buffer_Tail  : Integer;   { Locn in Async_Buffer to get next char }
  109.   Async_Buffer_NewTail : Integer;
  110.  
  111. {----------------------------------------------------------------------------}
  112. {                          USER CALLABLE ROUTINES                            }
  113. {----------------------------------------------------------------------------}
  114.  
  115. { procedure Async_Init; This has been moved to the Unit initialization }
  116. { initialize variables }
  117.  
  118. procedure Async_Close;
  119. { reset the interrupt system when UART interrupts no longer needed }
  120.  
  121. function Async_Open(ComPort       : Integer;
  122.                     BaudRate      : Integer;
  123.                     Parity        : Char;
  124.                     WordSize      : Integer;
  125.                     StopBits      : Integer) : Boolean;
  126. { open a communications port }
  127.  
  128. function Async_Buffer_Check(var C : Char) : Boolean;
  129. { see if a character has been received; return it if yes }
  130.  
  131. procedure Async_Send(C : Char);
  132. { transmit a character }
  133.  
  134. procedure Async_Send_String(S : LStr);
  135. { transmit a string }
  136.  
  137. {----------------------------------------------------------------------------}
  138.  
  139. IMPLEMENTATION
  140.  
  141.  
  142. const
  143.   UART_THR = $00;    { offset from base of UART Registers for IBM PC }
  144.   UART_RBR = $00;
  145.   UART_IER = $01;
  146.   UART_IIR = $02;
  147.   UART_LCR = $03;
  148.   UART_MCR = $04;
  149.   UART_LSR = $05;
  150.   UART_MSR = $06;
  151.  
  152.   I8088_IMR = $21;   { port address of the Interrupt Mask Register }
  153.  
  154.  
  155. var
  156.  
  157.   Async_BIOS_Port_Table : Array[1..2] of Integer absolute $40:0;
  158.                { This table is initialized by BIOS equipment determination
  159.                  code at boot time to contain the base addresses for the
  160.                  installed async adapters.  A value of 0 means "not in-
  161.                  stalled." }
  162.   Async_ExitProc_save: POINTER;              {ExitProc save pointer}
  163.  
  164. const
  165.   Async_Num_Bauds = 8;
  166.   Async_Baud_Table : array [1..Async_Num_Bauds] of record
  167.                                                      Baud, Bits : integer
  168.                                                    end
  169.                    = ((Baud:110;  Bits:$00),
  170.                       (Baud:150;  Bits:$20),
  171.                       (Baud:300;  Bits:$40),
  172.                       (Baud:600;  Bits:$60),
  173.                       (Baud:1200; Bits:$80),
  174.                       (Baud:2400; Bits:$A0),
  175.                       (Baud:4800; Bits:$C0),
  176.                       (Baud:9600; Bits:$E0));
  177.  
  178.  
  179. PROCEDURE DisableInterrupts; inline($FA {cli} );     {MACROS}
  180. PROCEDURE EnableInterrupts;  inline($FB {sti} );
  181.  
  182.  
  183. procedure BIOS_RS232_Init(ComPort, ComParm : Integer);
  184. { Issue Interrupt $14 to initialize the UART }
  185. { See the IBM PC Technical Reference Manual for the format of ComParm }
  186. var
  187.   Regs : registers;
  188. begin
  189.   with Regs do
  190.     begin
  191.       ax := ComParm and $00FF;  { AH=0; AL=ComParm }
  192.       dx := ComPort;
  193.       Intr($14, Regs)
  194.     end
  195. end; { BIOS_RS232_Init }
  196.  
  197.  
  198. {----------------------------------------------------------------------}
  199. {                                                                      }
  200. {  ISR - Interrupt Service Routine                                     }
  201. {                                                                      }
  202. {----------------------------------------------------------------------}
  203.  
  204. procedure Async_Isr;  INTERRUPT;
  205. { Interrupt Service Routine }
  206. { Invoked when the UART has received a byte of data from the
  207.   communication line }
  208.  
  209. { re-written 9/10/84 to be entirely in machine language; original source
  210.   left as comments }
  211.  
  212. begin
  213.  
  214.   Inline(
  215.     $FB/                           { STI }
  216.       { get the incomming character }
  217.       { Async_Buffer[Async_Buffer_Head] := Chr(Port[UART_RBR + Async_Base]); }
  218.     $8B/$16/Async_Base/            { MOV DX,Async_Base }
  219.     $EC/                           { IN AL,DX }
  220.     $8B/$1E/Async_Buffer_Head/     { MOV BX,Async_Buffer_Head }
  221.     $88/$87/Async_Buffer/          { MOV Async_Buffer[BX],AL }
  222.       { Async_Buffer_NewHead := Async_Buffer_Head + 1; }
  223.     $43/                           { INC BX }
  224.       { if Async_Buffer_NewHead > Async_Buffer_Max then
  225.           Async_Buffer_NewHead := 0; }
  226.     $81/$FB/Async_Buffer_Max/      { CMP BX,Async_Buffer_Max }
  227.     $7E/$02/                       { JLE L001 }
  228.     $33/$DB/                       { XOR BX,BX }
  229.       { if Async_Buffer_NewHead = Async_Buffer_Tail then
  230.           Async_Buffer_Overflow := TRUE
  231.         else }
  232. {L001:}
  233.     $3B/$1E/Async_Buffer_Tail/     { CMP BX,Async_Buffer_Tail }
  234.     $75/$08/                       { JNE L002 }
  235.     $C6/$06/Async_Buffer_Overflow/$01/ { MOV Async_Buffer_Overflow,1 }
  236.     $90/                           { NOP generated by assembler for some reason }
  237.     $EB/$16/                       { JMP SHORT L003 }
  238.       { begin
  239.           Async_Buffer_Head := Async_Buffer_NewHead;
  240.           Async_Buffer_Used := Async_Buffer_Used + 1;
  241.           if Async_Buffer_Used > Async_MaxBufferUsed then
  242.             Async_MaxBufferUsed := Async_Buffer_Used
  243.         end; }
  244. {L002:}
  245.     $89/$1E/Async_Buffer_Head/     { MOV Async_Buffer_Head,BX }
  246.     $FF/$06/Async_Buffer_Used/     { INC Async_Buffer_Used }
  247.     $8B/$1E/Async_Buffer_Used/     { MOV BX,Async_Buffer_Used }
  248.     $3B/$1E/Async_MaxBufferUsed/   { CMP BX,Async_MaxBufferUsed }
  249.     $7E/$04/                       { JLE L003 }
  250.     $89/$1E/Async_MaxBufferUsed/   { MOV Async_MaxBufferUsed,BX }
  251. {L003:}
  252.       { disable interrupts }
  253.     $FA/                           { CLI }
  254.       { Port[$20] := $20; }  { use non-specific EOI }
  255.     $B0/$20/                       { MOV AL,20h }
  256.     $E6/$20                        { OUT 20h,AL }
  257.        )
  258. end; { Async_Isr }
  259.  
  260. procedure Async_Close;
  261. { reset the interrupt system when UART interrupts no longer needed }
  262. var
  263.   i, m : Integer;
  264. begin
  265.   if Async_Open_Flag then
  266.     begin
  267.  
  268.       { disable the IRQ on the 8259 }
  269.       DisableInterrupts;
  270.       i := Port[I8088_IMR];        { get the interrupt mask register }
  271.       m := 1 shl Async_Irq;        { set mask to turn off interrupt }
  272.       Port[I8088_IMR] := i or m;
  273.  
  274.       { disable the 8250 data ready interrupt }
  275.       Port[UART_IER + Async_Base] := 0;
  276.  
  277.       { disable OUT2 on the 8250 }
  278.       Port[UART_MCR + Async_Base] := 0;
  279.       EnableInterrupts;
  280.  
  281.       SetIntVec(Async_Irq + 8,Async_OriginalVector);
  282.  
  283.       { re-initialize our data areas so we know the port is closed }
  284.       Async_Open_Flag := FALSE
  285.  
  286.     end
  287. end; { Async_Close }
  288.  
  289. function Async_Open(ComPort       : Integer;
  290.                     BaudRate      : Integer;
  291.                     Parity        : Char;
  292.                     WordSize      : Integer;
  293.                     StopBits      : Integer) : Boolean;
  294. { open a communications port }
  295. var
  296.   ComParm : Integer;
  297.   i, m : Integer;
  298. begin
  299.   if Async_Open_Flag then Async_Close;
  300.  
  301.   if (ComPort = 2) and (Async_BIOS_Port_Table[2] <> 0) then
  302.     Async_Port := 2
  303.   else
  304.     Async_Port := 1;  { default to COM1 }
  305.   Async_Base := Async_BIOS_Port_Table[Async_Port];
  306.   Async_Irq := Hi(Async_Base) + 1;
  307.  
  308.   if (Port[UART_IIR + Async_Base] and $00F8) <> 0 then
  309.     Async_Open := FALSE
  310.   else
  311.     begin
  312.       Async_Buffer_Head := 0;
  313.       Async_Buffer_Tail := 0;
  314.       Async_Buffer_Overflow := FALSE;
  315.  
  316.   { Build the ComParm for RS232_Init }
  317.   { See Technical Reference Manual for description }
  318.  
  319.       ComParm := $0000;
  320.  
  321.   { Set up the bits for the baud rate }
  322.       i := 0;
  323.       repeat
  324.         i := i + 1
  325.       until (Async_Baud_Table[i].Baud = BaudRate) or (i = Async_Num_Bauds);
  326.       ComParm := ComParm or Async_Baud_Table[i].Bits;
  327.  
  328.       if Parity in ['E', 'e'] then ComParm := ComParm or $0018
  329.       else if Parity in ['O', 'o'] then ComParm := ComParm or $0008
  330.       else ComParm := ComParm or $0000;  { default to No parity }
  331.  
  332.       if WordSize = 7 then ComParm := ComParm or $0002
  333.       else ComParm := ComParm or $0003;  { default to 8 data bits }
  334.  
  335.       if StopBits = 2 then ComParm := ComParm or $0004
  336.       else ComParm := ComParm or $0000;  { default to 1 stop bit }
  337.  
  338.   { use the BIOS COM port initialization routine to save typing the code }
  339.       BIOS_RS232_Init(Async_Port - 1, ComParm);
  340.  
  341.       GetIntVec(Async_Irq + 8, Async_OriginalVector);
  342.       SetIntVec(Async_Irq + 8, @Async_Isr);
  343.  
  344.   { read the RBR and reset any possible pending error conditions }
  345.   { first turn off the Divisor Access Latch Bit to allow access to RBR, etc. }
  346.  
  347.       DisableInterrupts;
  348.  
  349.       Port[UART_LCR + Async_Base] := Port[UART_LCR + Async_Base] and $7F;
  350.   { read the Line Status Register to reset any errors it indicates }
  351.       i := Port[UART_LSR + Async_Base];
  352.   { read the Receiver Buffer Register in case it contains a character }
  353.       i := Port[UART_RBR + Async_Base];
  354.  
  355.   { enable the irq on the 8259 controller }
  356.       i := Port[I8088_IMR];  { get the interrupt mask register }
  357.       m := (1 shl Async_Irq) xor $00FF;
  358.       Port[I8088_IMR] := i and m;
  359.  
  360.   { enable the data ready interrupt on the 8250 }
  361.       Port[UART_IER + Async_Base] := $01; { enable data ready interrupt }
  362.  
  363.   { enable OUT2 on 8250 }
  364.       i := Port[UART_MCR + Async_Base];
  365.       Port[UART_MCR + Async_Base] := i or $08;
  366.  
  367.       EnableInterrupts;
  368.       Async_Open_Flag := TRUE;  { bug fix by Scott Herr }
  369.       Async_Open := TRUE
  370.     end
  371. end; { Async_Open }
  372.  
  373. function Async_Buffer_Check(var C : Char) : Boolean;
  374. { see if a character has been received; return it if yes }
  375. begin
  376.   if Async_Buffer_Head = Async_Buffer_Tail then
  377.     Async_Buffer_Check := FALSE
  378.   else
  379.     begin
  380.       C := Async_Buffer[Async_Buffer_Tail];
  381.       Async_Buffer_Tail := Async_Buffer_Tail + 1;
  382.       if Async_Buffer_Tail > Async_Buffer_Max then
  383.         Async_Buffer_Tail := 0;
  384.       Async_Buffer_Used := Async_Buffer_Used - 1;
  385.       Async_Buffer_Check := TRUE
  386.     end
  387. end; { Async_Buffer_Check }
  388.  
  389. procedure Async_Send(C : Char);
  390. { transmit a character }
  391. var
  392.   i, m, counter : Integer;
  393. begin
  394.   Port[UART_MCR + Async_Base] := $0B; { turn on OUT2, DTR, and RTS }
  395.  
  396.   { wait for CTS }
  397.   counter := MaxInt;
  398.   while (counter <> 0) and ((Port[UART_MSR + Async_Base] and $10) = 0) do
  399.     counter := counter - 1;
  400.  
  401.   { wait for Transmit Hold Register Empty (THRE) }
  402.   if counter <> 0 then counter := MaxInt;
  403.   while (counter <> 0) and ((Port[UART_LSR + Async_Base] and $20) = 0) do
  404.     counter := counter - 1;
  405.  
  406.   if counter <> 0 then
  407.     begin
  408.       { send the character }
  409.       DisableInterrupts;
  410.       Port[UART_THR + Async_Base] := Ord(C);
  411.       EnableInterrupts
  412.     end
  413.   else
  414.     writeln('<<<TIMEOUT>>>');
  415.  
  416. end; { Async_Send }
  417.  
  418. procedure Async_Send_String(S : LStr);
  419. { transmit a string }
  420. var
  421.   i : Integer;
  422. begin
  423.   for i := 1 to length(S) do
  424.     Async_Send(S[i])
  425. end; { Async_Send_String }
  426.  
  427. {$F+} {Force this to be far call}
  428. Procedure Async_ExitProc;
  429. {$F-}
  430. Begin
  431.    Async_close;                    {conditionally close ports}
  432.    ExitProc:=Async_ExitProc_save;  {restore original ExitProc ptr}
  433. End;
  434.  
  435. { Unit initialization - initialize variables }
  436. begin
  437.   Async_Open_Flag := FALSE;
  438.   Async_Buffer_Overflow := FALSE;
  439.   Async_Buffer_Used := 0;
  440.   Async_MaxBufferUsed := 0;
  441.   Async_ExitProc_save:=ExitProc;    {save original ExitProc ptr}
  442.   ExitProc:=@Async_ExitProc;        {Set our ExitProc in chain }
  443.  
  444. {$I+} { I/O checking ON}
  445. {$S+} { Stack checking ON}
  446. {$V+} { Var-str checking ON}
  447.  
  448. end. { ASYNC4U UNIT }
  449.